function TITTService() {
	var me = this;
	var ef = IOFactory.createEncodingFacade();

	var hostObj = new Object();

	var hostID;
	var registered = [];
	var registeredIDs = [];
	
	function createServiceObject(o, tokens, serviceID) {
		o.__titt__serviceID = serviceID;
		o.__titt__tokens = tokens;

		var sos = new ServiceObjectServer(o, new MethodInvoker(o), new MethodParser(), serviceID);

		registeredIDs[registeredIDs.length] = serviceID;
		registered[serviceID] = sos;
			
		return sos;
	}

	this.register = function(o, tokens, k) {
		ServiceUtil.init(function() {
			function kk() {
				doRegister(o, tokens, k);
			}
			if(!hostID)
				doRegister(hostObj, ServiceUtil.generateRegistrationKey(), kk);
			else 
				kk();
		})
	}

	function doRegister(o, tokens, k) {
		me.unregister(o, function() {
			if (!hostID) {
				ServerProxy.register(o, tokens, function (serviceID) {
					hostID = serviceID;

					var sos = createServiceObject(o, tokens, serviceID);			
					var internal = new TITTInternal();					
					
					ServerProxy.detectErrors(serviceID, function(){});				

					function handlePackage() {
						ServerProxy.withdraw(serviceID, invokeAndRespond);
					}

					function isNuderResponse(to) {
						var o = to.getObject();

						if(!o) return false;
						if(o.__titt__isNuderResponse) return true;

						return false;
					}

					function invokeAndRespond(pack) {
						if(pack) {
							
							var asos = registered[pack.getServiceID()];
							var mh = asos.getMethodParser().parse(pack.getHead());

							if(mh.getMethodName() == null) {
								ServerProxy.syncResponse(418, pack.getPackageID(), handlePackage);
								delete pack;
								return;
							}
							
							var res = internal.call(asos.getObject(), mh.getMethodName());

							if(res != null)
								ServerProxy.syncResponseContent(200, res, pack.getPackageID(), handlePackage);
							else {
	
								var params = new Array(), i = 0;
	
								for(var h = 0; h < mh.getParams().length; h++)
									params[i++] = mh.getParams()[h];
	
								if(mh.getHTTPMethod() == TITTValues.POST) {
									var pr = new POSTReader(pack.getTITTSocket(), ef, new FormHead(mh));
									
									var postParams = pr.readValues();
	
									for(var h = 0; h < postParams.length; h++)
										params[i++] = postParams[h];
								}
								//try catch here
								var stat = 200;
								try {
									res = asos.getMethodInvoker().call(mh.getMethodName(), params);
								}
								catch(err) {
									stat = 418;
									res = new TypedObject(Types.STRING, ""+err);								
								}
																
								if (isNuderResponse(res))
									res.getObject().writeToStream(pack.getPackageID(), handlePackage); //TODO: error handling
								else 
									ServerProxy.syncResponseContent(stat, res, pack.getPackageID(), handlePackage);
															
								//tidy
								delete stat; delete params;
							}
							//method tidy
							delete res; delete pack;
						}
					}
					
					handlePackage();
					k();
					
				});
				
			}
			else 
				ServerProxy.registerWithLink(o, tokens, hostID, function(serviceID) {
					createServiceObject(o, tokens, serviceID);
					k();
				});
		
			
		});
		
	} 	
	
	function arrRemove(array, from, to) {
  		var rest = array.slice((to || from) + 1 || array.length);
  		array.length = from < 0 ? array.length + from : from;
  		return array.push.apply(array, rest);
	};
	
	this.lazyUnregister = function(o, k) {
		doUnregister(o,true, k);
	}
	
	this.unregister = function(o, k) {
		doUnregister(o,false, k);
	}
	
	function doUnregister(o, lazy, k) {
		if(o.__titt__serviceID) {
			var sid = o.__titt__serviceID;
			o.__titt__serviceID = null;
			o.__titt__tokens = null;
			
			for (var i = 0; i < registeredIDs.length; i++) 
				if (registeredIDs[i] == sid) {
					registered[registeredIDs[i]] = null;
					arrRemove(registeredIDs, i)
					break;
				}
			
			ServerProxy.unregister(sid, lazy, function() {
				if(registeredIDs.length == 1) 
					doUnregister(hostObj, false, function() {
						hostID = null;
						hostObj = new Object();
						k();
					});
				else
					k();
			});	
		}
		else {
			k();
		}
		
	}	
}